import * as fs from 'fs';
import * as path from 'path';
import { ApiEndpoint, ApiParameter, ApiExample } from '../extension';

export class PythonParser {
  async parseFile(filePath: string): Promise<ApiEndpoint[]> {
    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');
    const endpoints: ApiEndpoint[] = [];

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      // Look for Flask/FastAPI decorators
      if (line.includes('@app.route') || 
          line.includes('@router.') ||
          line.includes('@api.') ||
          line.includes('@bp.route')) {
        
        const endpoint = await this.parseEndpoint(lines, i, filePath);
        if (endpoint) {
          endpoints.push(endpoint);
        }
      }
    }

    return endpoints;
  }

  private async parseEndpoint(lines: string[], startLine: number, filePath: string): Promise<ApiEndpoint | null> {
    const line = lines[startLine];
    let method = 'GET';
    let path = '';
    let description = '';
    let parameters: ApiParameter[] = [];
    let returnType = 'dict';
    let returnDescription = '';

    // Extract path and method from decorator
    if (line.includes('@app.route') || line.includes('@router.') || line.includes('@bp.route')) {
      path = this.extractPath(line);
      method = this.extractMethod(line);
    } else if (line.includes('@api.')) {
      // FastAPI specific
      path = this.extractFastAPIPath(line);
      method = this.extractFastAPIMethod(line);
    }

    // Look for function definition in next few lines
    let functionName = '';
    for (let i = startLine + 1; i < Math.min(startLine + 10, lines.length); i++) {
      const funcLine = lines[i];
      if (funcLine.includes('def ') && funcLine.includes('(')) {
        functionName = this.extractFunctionName(funcLine);
        parameters = this.extractPythonParameters(lines, i, funcLine);
        returnType = this.extractPythonReturnType(funcLine);
        break;
      }
    }

    // Look for docstring
    for (let i = startLine + 1; i < Math.min(startLine + 20, lines.length); i++) {
      const docLine = lines[i];
      if (docLine.includes('"""') || docLine.includes("'''")) {
        description = this.extractDocstring(lines, i);
        break;
      }
    }

    if (!functionName) {
      return null;
    }

    const examples = this.generateExamples(method, path, parameters, returnType);

    return {
      name: functionName,
      path,
      method,
      description: description || `${method} endpoint for ${functionName}`,
      parameters,
      returnType,
      returnDescription,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private extractPath(line: string): string {
    const pathMatch = line.match(/["']([^"']+)["']/);
    return pathMatch ? pathMatch[1] : '/';
  }

  private extractMethod(line: string): string {
    if (line.includes("methods=['POST']") || line.includes('methods=["POST"]')) return 'POST';
    if (line.includes("methods=['PUT']") || line.includes('methods=["PUT"]')) return 'PUT';
    if (line.includes("methods=['DELETE']") || line.includes('methods=["DELETE"]')) return 'DELETE';
    if (line.includes("methods=['PATCH']") || line.includes('methods=["PATCH"]')) return 'PATCH';
    return 'GET';
  }

  private extractFastAPIPath(line: string): string {
    const pathMatch = line.match(/@api\.(get|post|put|delete|patch)\(["']([^"']+)["']/);
    return pathMatch ? pathMatch[2] : '/';
  }

  private extractFastAPIMethod(line: string): string {
    if (line.includes('@api.get')) return 'GET';
    if (line.includes('@api.post')) return 'POST';
    if (line.includes('@api.put')) return 'PUT';
    if (line.includes('@api.delete')) return 'DELETE';
    if (line.includes('@api.patch')) return 'PATCH';
    return 'GET';
  }

  private extractFunctionName(line: string): string {
    const match = line.match(/def\s+(\w+)\s*\(/);
    return match ? match[1] : 'unknown';
  }

  private extractPythonParameters(lines: string[], funcLineIndex: number, funcLine: string): ApiParameter[] {
    const params: ApiParameter[] = [];
    const paramMatch = funcLine.match(/def\s+\w+\s*\(([^)]*)\)/);
    
    if (paramMatch) {
      const paramString = paramMatch[1];
      const paramList = paramString.split(',').map(p => p.trim());
      
      for (const param of paramList) {
        if (param && param !== '' && param !== 'self' && param !== 'cls') {
          const paramInfo = this.parsePythonParameterInfo(param, lines, funcLineIndex);
          if (paramInfo) {
            params.push(paramInfo);
          }
        }
      }
    }
    
    return params;
  }

  private parsePythonParameterInfo(paramString: string, lines: string[], funcLineIndex: number): ApiParameter | null {
    // Extract parameter name and type annotation
    const paramMatch = paramString.match(/^(\w+)(?:\s*:\s*([^=]+))?(?:\s*=\s*(.+))?$/);
    if (!paramMatch) return null;

    const name = paramMatch[1];
    const type = paramMatch[2] ? paramMatch[2].trim() : 'Any';
    const defaultValue = paramMatch[3] ? paramMatch[3].trim() : undefined;

    // Determine parameter source and extract additional info
    let source = 'body';
    let description = `Parameter ${name} of type ${type}`;
    let required = !defaultValue;

    // Check for FastAPI specific parameter types
    if (type.includes('Path(')) {
      source = 'path';
      const pathMatch = type.match(/Path\([^)]*\)/);
      if (pathMatch) {
        description = `Path parameter: ${name}`;
        // Extract additional Path parameters
        const requiredMatch = pathMatch[0].match(/required\s*=\s*(True|False)/);
        if (requiredMatch) {
          required = requiredMatch[1] === 'True';
        }
      }
    } else if (type.includes('Query(')) {
      source = 'query';
      const queryMatch = type.match(/Query\([^)]*\)/);
      if (queryMatch) {
        description = `Query parameter: ${name}`;
        // Extract additional Query parameters
        const requiredMatch = queryMatch[0].match(/required\s*=\s*(True|False)/);
        if (requiredMatch) {
          required = requiredMatch[1] === 'True';
        }
        const defaultMatch = queryMatch[0].match(/default\s*=\s*([^,)]+)/);
        if (defaultMatch) {
          description += ` (default: ${defaultMatch[1]})`;
        }
      }
    } else if (type.includes('Header(')) {
      source = 'header';
      const headerMatch = type.match(/Header\([^)]*\)/);
      if (headerMatch) {
        description = `Header: ${name}`;
      }
    } else if (type.includes('Body(')) {
      source = 'body';
      const bodyMatch = type.match(/Body\([^)]*\)/);
      if (bodyMatch) {
        description = `Request body: ${name}`;
      }
    } else if (type.includes('Form(')) {
      source = 'form';
      description = `Form data: ${name}`;
    } else if (type.includes('File(')) {
      source = 'file';
      description = `File upload: ${name}`;
    }

    // Look for parameter documentation in docstring
    const paramDoc = this.extractParameterDocumentation(lines, funcLineIndex, name);
    if (paramDoc) {
      description = paramDoc;
    }

    return {
      name,
      type,
      description,
      required,
      defaultValue,
      source
    };
  }

  private extractParameterDocumentation(lines: string[], funcLineIndex: number, paramName: string): string | null {
    // Look for parameter documentation in docstring
    for (let i = funcLineIndex + 1; i < Math.min(funcLineIndex + 30, lines.length); i++) {
      const line = lines[i];
      if (line.includes('"""') || line.includes("'''")) {
        // Search through the docstring block
        let inDocstring = false;
        for (let j = i; j < lines.length; j++) {
          const docLine = lines[j];
          if (docLine.includes('"""') || docLine.includes("'''")) {
            if (!inDocstring) {
              inDocstring = true;
            } else {
              break;
            }
          } else if (inDocstring) {
            // Look for parameter documentation patterns
            const paramMatch = docLine.match(new RegExp(`\\b${paramName}\\s*[:\\-]\\s*(.+)`, 'i'));
            if (paramMatch) {
              return paramMatch[1].trim();
            }
            // Also check for :param: format
            const paramMatch2 = docLine.match(new RegExp(`:param\\s+${paramName}:\\s*(.+)`, 'i'));
            if (paramMatch2) {
              return paramMatch2[1].trim();
            }
          }
        }
        break;
      }
    }
    return null;
  }

  private extractPythonReturnType(line: string): string {
    const returnMatch = line.match(/->\s*([^:]+)/);
    return returnMatch ? returnMatch[1].trim() : 'dict';
  }

  private extractDocstring(lines: string[], startLine: number): string {
    let description = '';
    let inDocstring = false;
    
    for (let i = startLine; i < lines.length; i++) {
      const line = lines[i];
      
      if (line.includes('"""') || line.includes("'''")) {
        if (!inDocstring) {
          inDocstring = true;
          const content = line.replace(/^.*["']{3}/, '').replace(/["']{3}.*$/, '').trim();
          if (content) description += content + ' ';
        } else {
          break;
        }
      } else if (inDocstring) {
        description += line.trim() + ' ';
      }
    }
    
    return description.trim();
  }

  private generateExamples(method: string, path: string, parameters: ApiParameter[], returnType: string): ApiExample[] {
    const examples: ApiExample[] = [];

    // cURL example
    let curlExample = `curl -X ${method} "http://localhost:5000${path}"`;
    
    // Add headers if needed
    const headers: string[] = [];
    const bodyParams: ApiParameter[] = [];
    const queryParams: ApiParameter[] = [];
    const pathParams: ApiParameter[] = [];
    const formParams: ApiParameter[] = [];

    parameters.forEach(param => {
      if (param.source === 'header') {
        headers.push(`-H "${param.name}: value"`);
      } else if (param.source === 'body') {
        bodyParams.push(param);
      } else if (param.source === 'query') {
        queryParams.push(param);
      } else if (param.source === 'path') {
        pathParams.push(param);
      } else if (param.source === 'form') {
        formParams.push(param);
      }
    });

    // Add query parameters
    if (queryParams.length > 0) {
      const queryString = queryParams.map(p => `${p.name}=value`).join('&');
      curlExample = curlExample.replace('"', `"?${queryString}"`);
    }

    // Add headers
    if (headers.length > 0) {
      curlExample += ` \\\n  ${headers.join(' \\\n  ')}`;
    }

    // Add body for POST/PUT/PATCH
    if (method !== 'GET' && (bodyParams.length > 0 || formParams.length > 0)) {
      if (bodyParams.length > 0) {
        curlExample += ` \\\n  -H "Content-Type: application/json" \\\n  -d '{\n`;
        const paramData = bodyParams.map(p => `    "${p.name}": "value"`).join(',\n');
        curlExample += paramData + '\n  }';
      } else if (formParams.length > 0) {
        curlExample += ` \\\n  -H "Content-Type: application/x-www-form-urlencoded" \\\n  -d "${formParams.map(p => `${p.name}=value`).join('&')}"`;
      }
    }

    examples.push({
      title: 'cURL Request',
      description: 'Example cURL request for this endpoint',
      code: curlExample,
      language: 'bash'
    });

    // Python requests example
    let pythonExample = `import requests\n\n`;
    
    if (method === 'GET') {
      pythonExample += `response = requests.get("http://localhost:5000${path}")`;
    } else {
      pythonExample += `url = "http://localhost:5000${path}"\n`;
      
      if (bodyParams.length > 0) {
        pythonExample += `data = {\n`;
        bodyParams.forEach(p => {
          pythonExample += `    "${p.name}": "value",\n`;
        });
        pythonExample += `}\n`;
        pythonExample += `response = requests.${method.toLowerCase()}(url, json=data)`;
      } else if (formParams.length > 0) {
        pythonExample += `data = {\n`;
        formParams.forEach(p => {
          pythonExample += `    "${p.name}": "value",\n`;
        });
        pythonExample += `}\n`;
        pythonExample += `response = requests.${method.toLowerCase()}(url, data=data)`;
      } else {
        pythonExample += `response = requests.${method.toLowerCase()}(url)`;
      }
    }
    pythonExample += `\n\nprint(response.status_code)\nprint(response.json())`;

    examples.push({
      title: 'Python Requests',
      description: 'Example using Python requests library',
      code: pythonExample,
      language: 'python'
    });

    return examples;
  }
} 